﻿using System;
using System.Collections.Generic;
using System.Collections;
using Bling;
using Semantics = Bling.Semantics;
using Bling.Vecs;
using Bling.Util;
using Bling.DSL;
using Bling.Properties;
using Bling.Core;
using Bling.Matrices;

// (a) Instancing: multiple buffers associated with processing of vertex buffer.
// (b) One vertex buffer and multiple shaders.
// (c) Multiple vertex buffers and multiple shaders. 
// 

namespace Bling.Core {
  using Bling.Vecs;
  public abstract partial class ColorBl<T, BRAND, ARITY> : MultiDoubleBl<T, BRAND, Vec4<bool>, Bool4Bl, ARITY>, HasXYZW<DoubleBl>, IColorBl<BRAND>
    where BRAND : ColorBl<T, BRAND, ARITY>
    where ARITY : Arity4<double, T, ARITY>, new() {
    public override Expr<T> Underlying {
      get {
        return Semantics.Color.Instance.Transform(base.Underlying);
      }
    }
  }
}


namespace Bling3D {
  public interface IVertexBuffer { }
  public abstract class PerVertexProperty {
    public readonly string Name;
    public PerVertexProperty(string Name) { this.Name = Name; }
    internal abstract void Append(BaseVertexBuffer Buffer, Brand Value);

  }
  public class PerVertexProperty<BRAND> : PerVertexProperty where BRAND : Brand<BRAND> {
    public PerVertexProperty(string Name) : base(Name) { }
    public BRAND this[IVertex Vertex] { get { return Vertex.Buffer.Get(this, Vertex.Index); } }
    internal override void Append(BaseVertexBuffer Buffer, Brand Value) {
      Buffer.Append<BRAND>(this, (BRAND)Value);
    }
  }
  public abstract class VertexInit {
    public static readonly PerVertexProperty<PointBl> UV = new PerVertexProperty<PointBl>("UV");
    public static readonly PerVertexProperty<Point3DBl> UVW = new PerVertexProperty<Point3DBl>("UVW");
    public static readonly PerVertexProperty<Point3DBl> Normal = new PerVertexProperty<Point3DBl>("Normal");

    public interface IHasUV<SPEC> : IVertexInit<SPEC> where SPEC : IHasUV<SPEC> {
      PointBl UV { set; }
    }
    public interface IHasUVW<SPEC> : IVertexInit<SPEC> where SPEC : IHasUVW<SPEC> {
      Point3DBl UVW { set; }
    }
    public interface IHasNormal<SPEC> : IVertexInit<SPEC> where SPEC : IHasNormal<SPEC> {
      Point3DBl Normal { set; }
    }
  }
  public interface IVertexInit<SPEC> where SPEC : IVertexInit<SPEC> {
    Point3DBl Position { set; }
  }
  public abstract class VertexInit<SPEC> : VertexInit, IVertexInit<SPEC> where SPEC : IVertexInit<SPEC> {
    public Point3DBl Position { internal get; set; }
    internal protected readonly Dictionary<PerVertexProperty, Brand> Data = new Dictionary<PerVertexProperty, Brand>();
    public VertexInit() { }
  }
  public class DefaultVertexInit : VertexInit<DefaultVertexInit> {
    public static implicit operator DefaultVertexInit(Point3DBl Point) {
      return new DefaultVertexInit() { Position = Point };
    }
  }
  public abstract class BaseVertexBuffer : IVertexBuffer {
    public readonly List<Point3DBl> Vertices = new List<Point3DBl>(); // we make the spec available during shading?
    public readonly List<Int3Bl> Indices = new List<Int3Bl>();
    protected int VertexCount { get { return Vertices.Count; } }

    private readonly Dictionary<PerVertexProperty, IList> PerVertex = new Dictionary<PerVertexProperty, IList>();
    private readonly Dictionary<PerVertexProperty, Brand[]> PerVertex0 = new Dictionary<PerVertexProperty, Brand[]>();
    private bool Frozen = false;

    internal BRAND Get<BRAND>(PerVertexProperty<BRAND> Property, IntBl Idx) where BRAND : Brand<BRAND> {
      Frozen = true;
      if (!PerVertex.ContainsKey(Property)) throw new Exception("Doesn't exist");
      if (!PerVertex0.ContainsKey(Property)) {
        var array = ((List<BRAND>)PerVertex[Property]).ToArray();
        PerVertex0[Property] = array;
        (array.Length == Vertices.Count).Assert();
      }
      return ((BRAND[])PerVertex0[Property]).Table(Idx);
    }
    internal void Append<BRAND>(PerVertexProperty<BRAND> Property, BRAND Value) where BRAND : Brand<BRAND> {
      if (Frozen) throw new AccessViolationException("Already frozen!");
      if (!PerVertex.ContainsKey(Property)) PerVertex[Property] = new List<BRAND>();
      var list = (List<BRAND>)PerVertex[Property];
      list.Add(Value);
    }
  }
  // when we create a vertex, we specify a bunch of positions

  public class VertexBuffer<SPEC> : BaseVertexBuffer where SPEC : IVertexInit<SPEC> {
    internal void Add(SPEC Spec0) {
      var Spec = (VertexInit<SPEC>)(object)Spec0;
      Vertices.Add(Spec.Position);
      foreach (var v in Spec.Data) v.Key.Append(this, v.Value);
    }
    public void Poly(params SPEC[] Ps) {
      (Ps.Length >= 3).Assert();
      Indices.Add(VertexCount + 0, VertexCount + 1, VertexCount + 2);
      var A = VertexCount + 0;
      var B = VertexCount + 2;
      for (int i = 3; i < Ps.Length; i++) {
        var C = VertexCount + i;
        Indices.Add(A, B, C);
        B = C;
      }
      // update vertex count.
      for (int i = 0; i < Ps.Length; i++) Add(Ps[i]);
    }
    public void Grid(int ColumnN, int RowN, Func<int, int, SPEC> F) {
      // 0  1  2  3  ... CN
      int VertexCount = this.VertexCount; // freeze it.
      Func<int, int, int> VertexAt = (ColumnM, RowM) => {
        (ColumnM <= ColumnN).Assert();
        (RowM <= RowN).Assert();
        return VertexCount + (RowM * (ColumnN + 1) + ColumnM);
      };
      {
        int at = VertexCount;
        for (int row = 0; row <= RowN; row++) {
          for (int col = 0; col <= ColumnN; col++) {
            (VertexAt(col, row) == at).Assert();
            Add(F(col, row));
            at += 1;
          }
        }
      }
      // initialize triangle indices
      for (int col = 0; col < ColumnN; col++) {
        for (int row = 0; row < RowN; row++) {
          var A = VertexAt(col + 0, row + 1);
          var B = VertexAt(col + 0, row + 0);
          var C = VertexAt(col + 1, row + 0);
          Indices.Add(A, B, C);
          var D = VertexAt(col + 1, row + 1);
          Indices.Add(A, C, D);
        }
      }
    }
    public void Grid(int ColumnN, int RowN, PointBl LeftTop, PointBl Size, Func<int, int, PointBl, SPEC> F) {
      var Size0 = Size / new PointBl(ColumnN, RowN);
      Grid(ColumnN, RowN, (col, row) => {
        return F(col, row, LeftTop + Size0 * new PointBl(col, row));
      });
    }
  }
  public class VertexBuffer : VertexBuffer<DefaultVertexInit> { }
  public interface IVertex {
    IntBl Index { get; } // used to access other resources....
    Point3DBl Position { get; set; }
    BaseVertexBuffer Buffer { get; }
  }
}

namespace Bling3D.Effects {

  // target for extension methods.
  public interface IVertex<SPEC> : IVertex where SPEC : IVertexInit<SPEC> {
    new VertexBuffer<SPEC> Buffer { get; }
  }
  public static class VertexExtensions {
    public static PointBl UV<SPEC>(this IVertex<SPEC> Vertex) where SPEC : VertexInit.IHasUV<SPEC> {
      return Semantics.TexCoord.Instance.Transform(VertexInit.UV[Vertex]);
    }
    public static Point3DBl UVW<SPEC>(this IVertex<SPEC> Vertex) where SPEC : VertexInit.IHasUVW<SPEC> {
      return Semantics.TexCoord.Instance.Transform(VertexInit.UVW[Vertex]);
    }
    public static Point3DBl Normal<SPEC>(this IVertex<SPEC> Vertex) where SPEC : VertexInit.IHasNormal<SPEC> {
      return Semantics.Normal.Instance.Transform(VertexInit.Normal[Vertex]);
    }
  }
  public class ShadedBuffers { // collection of shaded vertex buffers.
    public readonly List<VertexBufferEntry> Entries = new List<VertexBufferEntry>();
    public class VertexBufferEntry  {
      public BaseVertexBuffer Buffer { get; set; }
      public ColorBl Shader { get; set; }
      public IVertex Vertex { get; set; }
    }
    protected virtual IVertex Make(BaseVertexBuffer Buffer) {
      return new Vertex() { Buffer = Buffer };
    }
    protected virtual IVertex<SPEC> Make<SPEC>(VertexBuffer<SPEC> Buffer) where SPEC : IVertexInit<SPEC> {
      return new Vertex<SPEC>() { Buffer = Buffer };
    }
    public virtual void Shade<SPEC>(VertexBuffer<SPEC> Buffer, Func<IVertex<SPEC>, ColorBl> Shader) where SPEC : IVertexInit<SPEC> {
      var vertex = Make(Buffer);
      Entries.Add(new VertexBufferEntry() {
        Buffer = Buffer,
        Shader = Shader(vertex),
        Vertex = vertex,

      });
    }

    /*
    public void Add<SPEC>(VertexBuffer<SPEC> Buffer, Func<IVertex<SPEC>, ColorBl> Shader) where SPEC : IVertexInit<SPEC> {
      var v = Make<SPEC>(Buffer);
      Entries.Add(new VertexBufferEntry() { Buffer = Buffer, Shader =  Shader(v), Vertex = v });
    }
    public void Add<ELEM>(BaseVertexBuffer Buffer, IListBl<ELEM> Instances, Func<ELEM, IVertex, ColorBl> Shader) {
      throw new NotImplementedException();
      //Entries.Add(new VertexBufferEntry<ELEM>() { Buffer = Buffer, Shader = Shader, Instances = Instances});
    }
    public void Add<ELEM>(BaseVertexBuffer Vertices, ELEM[] Instances, Func<ELEM, IVertex, ColorBl> Shader) { throw new NotImplementedException(); }

    // two layers of instances.
    public void AddX<ELEMA, ELEMB>(BaseVertexBuffer Vertices, IListBl<ELEMA> LevelA, Func<ELEMA, IListBl<ELEMB>> LevelB,
      Func<ELEMA, ELEMB, IVertex, ColorBl> Shader) { throw new NotImplementedException(); }

    // three layers of instances, kind of rediculous.
    public void AddX<ELEMA, ELEMB, ELEMC>(BaseVertexBuffer Vertices,
      IListBl<ELEMA> LevelA, Func<ELEMA, IListBl<ELEMB>> LevelB, Func<ELEMB, IListBl<ELEMC>> LevelC,
        Func<ELEMA, ELEMB, ELEMC, IVertex, ColorBl> Shader) { throw new NotImplementedException(); }
     */
  }
  // force expression to evaluate in pixel shader!
  class PixelExpr<T> : ProxyExpr<T> {
    public PixelExpr(Expr<T> Underlying) : base(Underlying) { }
    protected override ProxyExpr<T> Make(Expr<T> e) {
      return new PixelExpr<T>(e);
    }
    protected override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is PixelShaderEval<EVAL>) {
        var info = (PixelShaderEval<EVAL>.MyInfo<T>)txt.DoEval(Underlying);
        if (!info.InShader) ((PixelShaderEval<EVAL>)txt).Registers.BringIn(Underlying, info);
        return info;
      }
      return base.Eval<EVAL>(txt);
    }
  }
  public static class VerticesExtensions {
    public static BRAND PerPixel<T,BRAND>(this Brand<T,BRAND> Value) where BRAND : Brand<T,BRAND> {
      return Brand<T, BRAND>.ToBrand(new PixelExpr<T>(Value.Underlying));
    }
  }
}
namespace Bling3D.Effects {
  using Bling.Shaders;
  using Bling.DSL;



  class VertexIndex : LoopIndexExpr {
    public VertexIndex(BaseVertexBuffer Buffer) : base(Buffer, Buffer.Vertices.Count) {
      this.Name = "Idx";
    }
    protected override Eval<EVAL>.Info<int> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is PixelShaderEval<EVAL>)
        return new PixelShaderEval<EVAL>.MyInfo<int>((PixelShaderEval<EVAL>) txt) { InShaderStatus = false, Value = null };
      if (txt is VertexShaderEval<EVAL>)
        return new VertexShaderEval<EVAL>.MyInfo<int>((VertexShaderEval<EVAL>) txt) 
        { InShaderStatus = false, Value = null, Mode = ParamMode.VertexParam.Instance };
      return base.Eval<EVAL>(txt);
    }
  }
  class PositionExpr : Expr<Bling.Vecs.Vec3<double>> {
    private PositionExpr() { }
    public static PositionExpr Instance = new PositionExpr();
    public override string ToString() {
      return "input.Pos";
    }
    protected override Eval<EVAL>.Info<Bling.Vecs.Vec3<double>> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is VertexShaderEval<EVAL>) { // very simple. 
        var txt0 = (VertexShaderEval<EVAL>)txt;
        var ret = (VertexShaderEval<EVAL>.MyInfo<Vec3<double>>)txt0.NewInfo<Vec3<double>>("input.Pos");
        ret.InShaderStatus = true;
        return ret;
      } else if (txt is PixelShaderEval<EVAL>) { // not automatically in pixel shader. 
        var ret = (PixelShaderEval<EVAL>.MyInfo<Vec3<double>>) ((PixelShaderEval<EVAL>)txt).NewInfo<Vec3<double>>(null);
        ret.InShaderStatus = false;
        return ret;
      }
      throw new NotImplementedException();
    }
  }
  public class Vertex : IVertex {
    public BaseVertexBuffer Buffer { get; internal set; }
    public Point3DBl PositionUpdate { private set; get; }
    public Point3DBl Position { 
      get { return Semantics.Position.Instance.Transform((Point3DBl) PositionExpr.Instance); }
      set { PositionUpdate = Semantics.Position.Instance.Transform(value); }
    }
    public IntBl Index { get { return new VertexIndex(Buffer); } }
  }
  public class Vertex<SPEC> : Vertex, IVertex<SPEC> where SPEC : IVertexInit<SPEC> {
    public new VertexBuffer<SPEC> Buffer {
      get { return (VertexBuffer<SPEC>)base.Buffer; }
      internal set { base.Buffer = value; }
    }

  }

  public abstract class CoreShaderEval<EVAL> : ShaderEval<EVAL>, Bling.Semantics.IRoleEval<EVAL>, ITableEval<EVAL>, IDynamicEval<EVAL> where EVAL : Eval<EVAL> {
    private readonly Stack<Bling.Semantics.Role> Roles = new Stack<Bling.Semantics.Role>();
    public CoreShaderEval() {
      Roles.Push(Bling.Semantics.NoRole.Instance);
    }

    public Info<T> Eval<T, ROLE>(Bling.Semantics.RoleExpr<T, ROLE> Expr) where ROLE : Bling.Semantics.Role<ROLE>, new() {
      Roles.Push(Bling.Semantics.Role<ROLE>.Instance);
      var ret = (MyInfo<T>) DoEval(Expr.Underlying); // set the role.
      var popped = Roles.Pop();
      (popped.Equals(Bling.Semantics.Role<ROLE>.Instance)).Assert();
      return ret;
    }
    public new interface MyInfo : ShaderEval<EVAL>.MyInfo {
      Bling.Semantics.Role Role { get; set; }
    }
    public new class MyInfo<T> : ShaderEval<EVAL>.MyInfo<T>, MyInfo {
      public Bling.Semantics.Role Role { get; set; }
      public MyInfo(CoreShaderEval<EVAL> txt) { this.Role = txt.Roles.Peek(); }
    }
    public abstract Info<T> Dynamic<T>(DynamicMode Mode);
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(string Value) {
      var p = (ShaderEval<EVAL>.MyInfo<T>) base.NewInfo<T>(Value);
      return new MyInfo<T>(this) { Value = p.Value, InShaderStatus = p.InShaderStatus };
    }
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(string Value, Info Copy) {
      var p = (ShaderEval<EVAL>.MyInfo<T>)base.NewInfo<T>(Value, Copy);
      (((MyInfo)Copy).Role != null).Assert();
      return new MyInfo<T>(this) { Value = p.Value, InShaderStatus = p.InShaderStatus, Role = ((MyInfo) Copy).Role };
    }
    public Eval<EVAL>.Info<T> Table<T>(TableExpr<T> value) {
      var idx = (MyInfo<int>) this.DoEval(value.Index);
      if (!idx.InShader && idx.Value == null) {
        return NewInfo<T>(null, idx); // just pass it through. 
      }
      // not really sure what to do here. 
      throw new NotSupportedException();
    }
    public Eval<EVAL>.Info<T> Block<T>(T[] Values, Eval<EVAL>.Info<int> idx) {
      throw new NotSupportedException();
    }
    protected override BaseShaderEval<EVAL>.MyInfo<T> Prepare<T>(Func<BaseShaderEval<EVAL>.MyInfo<T>> Target, Expr[] Exprs, params Eval<EVAL>.Info[] Infos) {
      var ret = base.Prepare<T>(Target, Exprs, Infos);
      (((MyInfo)ret).Role != null).Assert();
      if (((MyInfo)ret).Role is Semantics.NoRole) {
        foreach (var info in Infos) {
          var info0 = (MyInfo)info;
          (info0.Role != null).Assert();
          if (!(info0.Role is Semantics.NoRole)) {
            ((MyInfo)ret).Role = info0.Role;
          }

        }
      }
      return ret;
    }
  }
  public abstract class PixelShaderEval<EVAL> : CoreShaderEval<EVAL>
    where EVAL : Eval<EVAL> {
    public virtual void Transfer<T>(MyInfo<T> From, MyInfo<T> To) {
      To.Value = From.Value;
      To.InShaderStatus = From.InShaderStatus;

      To.Role = From.Role;
      (To.Role != null).Assert();
    }
    public override Eval<EVAL>.Info<T> Dynamic<T>(DynamicMode Mode) {
      var result = (MyInfo<T>) NewInfo<T>(null);
      result.InShaderStatus = false;
      return result;
    }
  }
  public class InputLayout {
    public struct ParamInfo {
      public Expr Expression;
      public Semantic Semantic;
    }
    public readonly List<ParamInfo> Params = new List<ParamInfo>();
    public override int GetHashCode() {
      var hc = 0;
      foreach (var e in Params) hc += e.GetHashCode();
      return hc;
    }
    public override bool Equals(object obj) {
      if (obj is InputLayout) {
        var obj0 = (InputLayout)obj;
        if (Params.Count != obj0.Params.Count) return false;
        for (int i = 0; i < Params.Count; i++)
          if (!Params[i].Equals(obj0.Params[i])) return false;
        return true;
      }
      return base.Equals(obj);
    }
  }

  public struct Semantic {
    public string Name;
    public int Index;
    public override string ToString() {
      return Name + Index;
    }
  }

  // start here, generate color bl. 
  public abstract class PixelShaderEval<EVAL, VEVAL> : PixelShaderEval<EVAL>
    where EVAL : Eval<EVAL>
    where VEVAL : Eval<VEVAL> {
    // some of these are vertex argument, some are uniforms. 
    public readonly string ShaderCode;

    public readonly Dictionary<string, Expr> Uniforms = new Dictionary<string, Expr>();

    public readonly InputLayout Layout;

    private readonly VertexShaderEval<VEVAL, EVAL> VertexEval;
    public PixelShaderEval(int RenderIndex, ColorBl Color, VertexShaderEval<VEVAL, EVAL> VertexEval) {
      this.VertexEval = VertexEval;
      string PixelShaderCode;
      {
        var info = (MyInfo<Vec4<double>>)Block("retV", Color.Underlying);
        (info.Value == "retV" || info.Value == null).Assert();
        var popped = this.PopScope();
        (popped.Count == 0 && this.IsEmpty).Assert();
        (Registers.FlushRegisters.Length == 0).Assert();
        var output = new System.IO.StringWriter();
        output.WriteLine("float4 PS(PS_Input input) : VS_Target {");
        output.WriteLine("  float4 retV;");
        if (!info.InShader && info.Value == null) {
          Registers.BringIn<Vec4<double>>(Color, info);
          (info.Value != null && info.BlockBefore == null).Assert();
          output.WriteLine("  retV = " + info.Value + ";");
        } else Flatten("  ", info, output);
        output.WriteLine("  return retV;");
        output.WriteLine("}");
        PixelShaderCode = output.ToString();
      }
      string Decls;
      {
        Dictionary<Semantics.Role, int> RoleMap = new Dictionary<Semantics.Role, int>();
        Func<Semantics.Role, Type, Semantic> AllocateRole = (role, type) => {
          (role != null).Assert();
          if (role is Semantics.NoRole) role = Semantics.TexCoord.Instance;

          if (!RoleMap.ContainsKey(role)) RoleMap[role] = 0;
          else RoleMap[role] = RoleMap[role] + 1;
          return new Semantic() {
            Name = role.Name,
            Index = RoleMap[role],
          };
        };
        // position evaluation must always evaluate to input.Pos and be "InShader" by default!
        var output = new System.IO.StringWriter();
        // uniform declarations are easy.
        for (int i = 0; i < VertexEval.Registers.Uniforms.Count; i++) {
          var E = VertexEval.Registers.Uniforms[i];
          output.WriteLine(TypeNameFor(E.TypeOfT) + " U" + i + ";");
          Uniforms["U" + i] = E;
        }
        output.WriteLine("struct VS_Input {");
        output.WriteLine("  float4 Pos : POSITION;");
        // because vertex shaders already have one position.
        RoleMap[Semantics.Position.Instance] = 0;
        for (int i = 0; i < VertexEval.Registers.Inputs.Count; i++) {
          var E = VertexEval.Registers.Inputs[i];
          var info = (VertexShaderEval<VEVAL, EVAL>.MyInfo)VertexEval.Registers.RegisterAlloc(E);
          var semantic = AllocateRole(info.Role, E.TypeOfT);
          output.WriteLine("  " + TypeNameFor(E.TypeOfT) + " V" + i + " : " + semantic + ";");
          // so the vertex buffer can be loaded outside of the shader.
          Layout.Params.Add(new InputLayout.ParamInfo() { Expression = E, Semantic = semantic });
        }
        output.WriteLine("};");

        output.WriteLine("struct PS_Input {");
        output.WriteLine("  float4 Pos : SV_POSITION;");
        RoleMap.Clear();
        foreach (var pp in VertexEval.PixelParameters) {
          var info = pp.Value;
          output.WriteLine("  " + TypeNameFor(info.TypeOfT) + " " + pp.Key + " : " + AllocateRole(info.Role, info.TypeOfT) + ";");
        }
        output.WriteLine("};");
        Decls = output.ToString();
      }
      string VertexShaderCode;
      {
        var output = new System.IO.StringWriter();
        output.WriteLine("PS_Input VS(VS_Input input) {");
        output.WriteLine("  PS_Input output = (PS_Input) 0;");
        // handle all temporaries first.
        var Temps = VertexEval.PopScope();
        (VertexEval.IsEmpty).Assert();
        foreach (var temp in Temps) {
          var AsTemp = (VertexShaderEval<VEVAL, EVAL>.MyInfo)temp.AsTemp;
          var AsCode = (VertexShaderEval<VEVAL, EVAL>.MyInfo)temp.Code;
          VertexEval.Flatten("  ", AsCode, output);
          output.WriteLine("  " + TypeNameFor(AsTemp.TypeOfT) + " " + AsTemp.Value + " = " + AsCode.Value + ";");
        }
        // temps are out, now....assign all the pixel shader paramters, start with implicit position parameter.
        VertexEval.Flatten("  ", VertexEval.PositionUpdate, output);
        output.WriteLine("  output.Pos = " + VertexEval.PositionUpdate.Value + ";");
        foreach (var pp in VertexEval.PixelParameters) {
          var info = pp.Value;
          VertexEval.Flatten("  ", info, output);
          output.WriteLine("  output." + pp.Key + " = " + info.Value + ";");
        }
        output.WriteLine("  return output;");
        output.WriteLine("}");
        VertexShaderCode = output.ToString();
      }
      string TechCode;
      {
        var output = new System.IO.StringWriter();
        output.WriteLine("technique10 Render" + RenderIndex + " {");
        output.WriteLine("  pass P0 {");
        output.WriteLine("    SetVertexShader( CompileShader( vs_4_0, VS() ) );");
        output.WriteLine("    SetGeometryShader(NULL);");
        output.WriteLine("    SetPixelShader(CompileShader(ps_4_0, PS()));");
        output.WriteLine("  }");
        output.WriteLine("}");
        TechCode = output.ToString();
      }
      // all together
      {
        var output = new System.IO.StringWriter();
        output.WriteLine(Decls);
        output.WriteLine(VertexShaderCode);
        output.WriteLine(PixelShaderCode);
        output.WriteLine(TechCode);
        ShaderCode = output.ToString();
      }
    }
    public new class ShaderRegisterFile : ShaderEval<EVAL>.ShaderRegisterFile {
      public ShaderRegisterFile(EVAL Outer) : base(Outer) { }
      public override void BringIn<T>(Expr<T> Expr, BaseShaderEval<EVAL>.MyInfo<T> Info) {
        // forward to vertex shader immediately.
        ((PixelShaderEval<EVAL, VEVAL>)(object)Outer).VertexEval.PixelRegister(
          ((PixelShaderEval<EVAL, VEVAL>)(object)Outer), Expr, (MyInfo<T>)Info);
      }
      protected override void Copy<T>(BaseShaderEval<EVAL>.MyInfo<T> From, BaseShaderEval<EVAL>.MyInfo<T> To) {
        base.Copy<T>(From, To);
        ((MyInfo<T>)To).Role = ((MyInfo<T>)From).Role;
      }
    }
    protected override ShaderEval<EVAL>.ShaderRegisterFile MakeRegisterFile() {
      return new ShaderRegisterFile(this);
    }
    public new ShaderRegisterFile Registers { get { return (ShaderRegisterFile)base.Registers; } }
    public virtual string ParamName(VertexShaderEval<VEVAL, EVAL>.MyInfo Info, int Count) { return "P" + Count; }
  }
  public abstract class ParamMode {
    public abstract ParamMode Merge(ParamMode Other);
    public class Uniform : ParamMode {
      public static readonly Uniform Instance = new Uniform();
      private Uniform() { }
      public override ParamMode Merge(ParamMode Other) { return Other; }
    }
    public class VertexParam : ParamMode {
      public static readonly VertexParam Instance = new VertexParam();
      private VertexParam() { }
      public override ParamMode Merge(ParamMode Other) { return this; }
    }
  }


  public abstract class VertexShaderEval<EVAL> : CoreShaderEval<EVAL> where EVAL : Eval<EVAL> {
    public new interface MyInfo : CoreShaderEval<EVAL>.MyInfo {
      bool IsUniform { get; }
      ParamMode Mode { get; set; }
      DynamicMode Dynamic { get; set;  }
    }
    public new class MyInfo<T> : CoreShaderEval<EVAL>.MyInfo<T>, MyInfo {
      public ParamMode Mode { get; set; }
      public bool IsUniform { get { return Mode is ParamMode.Uniform; } }
      public DynamicMode Dynamic { get; set; }
      public MyInfo(VertexShaderEval<EVAL> txt) : base(txt) {
        Dynamic = DynamicMode.Unspecified;
      }
    }
    public override Eval<EVAL>.Info<T> Dynamic<T>(DynamicMode Mode) {
      var result = (MyInfo<T>)NewInfo<T>(null);
      result.InShaderStatus = false;
      result.Dynamic = Mode;
      result.Mode = ParamMode.Uniform.Instance;
      return result;
    }
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(string Value) {
      var p = (CoreShaderEval<EVAL>.MyInfo<T>)base.NewInfo<T>(Value);
      (p.Role != null).Assert();
      return new MyInfo<T>(this) { Value = p.Value, InShaderStatus = p.InShaderStatus, Role = p.Role, };
    }
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(string Value, Info Copy) {
      var p = (CoreShaderEval<EVAL>.MyInfo<T>)base.NewInfo<T>(Value, Copy);
      (p.Role != null).Assert();
      return new MyInfo<T>(this) { Value = p.Value, InShaderStatus = p.InShaderStatus, Role = p.Role, Mode = ((MyInfo) Copy).Mode };
    }
    protected override BaseShaderEval<EVAL>.MyInfo<T> Prepare<T>(Func<BaseShaderEval<EVAL>.MyInfo<T>> Target, Expr[] Exprs, params Eval<EVAL>.Info[] Infos) {
      var InShader = false;
      ParamMode Mode = null;
      DynamicMode Dynamic = DynamicMode.Unspecified;
      for (int i = 0; i < Infos.Length; i++ ) {
        var info0 = (MyInfo)Infos[i];
        if (info0.InShader) {
          InShader = true; break;
        } else if (info0.Mode != null) {
          if (Mode == info0.Mode || Mode == null) Mode = info0.Mode;
          else {
            Registers.BringIn(Exprs[i], info0);
            InShader = true;
            break;
          }
          if (info0.Dynamic > Dynamic) Dynamic = info0.Dynamic;
        } else { // must be constant.
          (info0.Value != null).Assert();
        }
      }
      var ret = (MyInfo)base.Prepare<T>(Target, Exprs, Infos);
      if (!InShader) {
        ret.Mode = Mode;
        ret.Dynamic = Dynamic;
      }
      return (MyInfo<T>) ret;
    }

    public new class ShaderRegisterFile : ShaderEval<EVAL>.ShaderRegisterFile {
      public readonly List<Expr> Uniforms = new List<Expr>();
      public readonly List<Expr> Inputs = new List<Expr>();
      public ShaderRegisterFile(EVAL eval) : base(eval) { }
      public override BaseShaderEval<EVAL>.MyInfo<T> BringInNew<T>(Expr<T> Expr, BaseShaderEval<EVAL>.MyInfo<T> Info) {
        var Info0 = (MyInfo<T>)Info;
        (Info0.Value == null && Info0.InShader).Assert();
        Info0 = (MyInfo<T>)base.BringInNew<T>(Expr, Info0);
        if (Info0.Mode is ParamMode.Uniform)
          Uniforms.Add(Expr);
        else if (Info0.Mode is ParamMode.VertexParam)
          Inputs.Add(Expr);
        else throw new NotSupportedException();
        return Info0;
      }
      protected override string RegisterName<T>(Expr<T> Expr, int Idx, BaseShaderEval<EVAL>.MyInfo<T> Info) {
        var Info0 = (MyInfo<T>)Info;
        if (Info0.Mode is ParamMode.Uniform) return "U" + Uniforms.Count;
        else if (Info0.Mode is ParamMode.VertexParam) {
          return "input.V" + Inputs.Count;
        } else throw new NotSupportedException();
      }
      protected override void Copy<T>(BaseShaderEval<EVAL>.MyInfo<T> From, BaseShaderEval<EVAL>.MyInfo<T> To) {
        base.Copy<T>(From, To);
        ((MyInfo<T>)To).Mode = ((MyInfo<T>)From).Mode;
        ((MyInfo<T>)To).Role = ((MyInfo<T>)From).Role;
        (((MyInfo<T>)To).Role != null).Assert();
      }
    }
    protected override ShaderEval<EVAL>.ShaderRegisterFile MakeRegisterFile() {
      return new ShaderRegisterFile(this);
    }
    public new ShaderRegisterFile Registers { get { return (ShaderRegisterFile)base.Registers; } }
  }
  public abstract class VertexShaderEval<EVAL, PEVAL> : VertexShaderEval<EVAL>
    where EVAL : Eval<EVAL>
    where PEVAL : Eval<PEVAL> {
    private readonly Dictionary<Expr, PixelShaderEval<PEVAL, EVAL>.MyInfo> ExistingPixel = new Dictionary<Expr, CoreShaderEval<PEVAL>.MyInfo>();
    internal readonly Dictionary<string, MyInfo> PixelParameters = new Dictionary<string, MyInfo>();
    public readonly MyInfo<Bling.Vecs.Vec3<double>> PositionUpdate;
    public VertexShaderEval(Expr<Bling.Vecs.Vec3<double>> PositionUpdate) {
      this.PositionUpdate = (MyInfo<Bling.Vecs.Vec3<double>>) DoEval(PositionUpdate);
    }
    public void PixelRegister<T>(PixelShaderEval<PEVAL, EVAL> txt, Expr<T> Expr, PixelShaderEval<PEVAL, EVAL>.MyInfo<T> Info) {
      if (ExistingPixel.ContainsKey(Expr)) {
        txt.Transfer((PixelShaderEval<PEVAL, EVAL>.MyInfo<T>)ExistingPixel[Expr], Info);
        return;
      }
      // don't bother pushing a new scope.
      var exprI = (MyInfo)DoEval(Expr);
      // not a constant.
      (exprI.InShader || exprI.Value == null).Assert();
      if (!exprI.InShader) Registers.BringIn(Expr, exprI);
      (exprI.InShader && exprI.Value != null).Assert();
      // is either a uniform or...a parameter
      if (!exprI.IsUniform) { // if a parameter.
        // pass as pixel shader parameter 
        var pname = txt.ParamName(exprI, PixelParameters.Count);
        Info.Value = "input." + pname;
        Info.InShaderStatus = true;
        PixelParameters[pname] = exprI;
      } else { // must be a uniform, use directly. 
        Info.Value = exprI.Value;
        Info.InShaderStatus = true;
      }
      ExistingPixel[Expr] = Info;
    }
  }
  public class VertexShaderEval : VertexShaderEval<VertexShaderEval, PixelShaderEval> {
    public VertexShaderEval(Expr<Bling.Vecs.Vec3<double>> PositionUpdate) : base(PositionUpdate) { }
  }
  public class PixelShaderEval : PixelShaderEval<PixelShaderEval, VertexShaderEval> {
    public PixelShaderEval(int i, ColorBl Color, Expr<Bling.Vecs.Vec3<double>> PositionUpdate) : base(i, Color, new VertexShaderEval(PositionUpdate)) { }
  }

}